package org.yu.team2023.flashdays;


import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.effect.MobEffectCategory;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.INBTSerializable;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.living.*;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.commons.lang3.RandomUtils;
import org.yu.team2023.flashdays.item.TreasureCurios;
import org.yu.team2023.flashdays.net.DamageParticlePackage;
import org.yu.team2023.flashdays.net.FlashNet;

import java.util.ArrayList;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

import static org.yu.team2023.flashdays.ModUtil.*;

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.FORGE, modid = FlashDays.MODID)
public class ForgeEventHandle {
    @SubscribeEvent(priority = EventPriority.LOWEST)
    public static void onLivingDeath(LivingDeathEvent event) {
        if (event.isCanceled()) return;
        runIfPlayerDamage(event.getSource(), (player, capability) -> {
            //可汗被动
            if (capability.isKeyOver4(ModItems.KHANS_THRONE.getSetName())) {
                int level = 0;
                if (player.hasEffect(ModMobEffects.KHAN.get()))
                    level = Math.min(4, player.getEffect(ModMobEffects.KHAN.get()).getAmplifier() + 1);
                player.addEffect(new MobEffectInstance(ModMobEffects.KHAN.get(), 20 * 60, level));
            }
        });
    }

    @SubscribeEvent
    public static void onLivingDrops(LivingDropsEvent event) {
        if (event.isCanceled() || event.getSource() == null) return;
        LivingEntity living = event.getEntity();
        var maxHealth = living.getMaxHealth();
        if (maxHealth < 100) return;
        //掉遗珍
        DamageSource source = event.getSource();
        Player player1 = null;
        if (source.getEntity() instanceof Player p) {
            player1 = p;
        } else if (source.getDirectEntity() instanceof Player p) {
            player1 = p;
        }
        if (player1 != null) {
            Player finalPlayer = player1;
            player1.getCapability(PlayerRPGCapabilityProvider.CAPABILITY).ifPresent(playerCuriosSetCapability -> {
                //掉遗珍
                int index = Mth.clamp((int) (maxHealth / 100) - 1, 0, TreasureQuality.values().length-1);
                TreasureQuality quality = TreasureQuality.values()[index];
                var itemSet = new ArrayList<>(TreasureSets.TREASURE_SETS_MAP.values());
                var stack = itemSet.get(RandomUtils.nextInt(0,itemSet.size())).unidentifieds.get(quality).get().getDefaultInstance();
                ItemEntity item = new ItemEntity(finalPlayer.level(), living.getX(), living.getEyeY(), living.getZ(),stack);
                event.getDrops().add(item);
            });
        }
    }

    @SubscribeEvent
    public static void onLivingUse(LivingEntityUseItemEvent.Start event) {
        if (event.isCanceled()) return;
        if (event.getEntity() instanceof Player player) {
            BSEntityUtil.safeAttribute(player, ModAttributes.USE_SPEED_UP, amount -> {
                event.setDuration(Math.max(1, (int) (Math.round(event.getDuration() * (1d - amount / 100d)))));
            });
        }
    }

    private static boolean isEntityWeakness(LivingEntity entity) {
        return (!entity.isInvulnerable()) && ((entity.isOnFire()) || entity.getTicksFrozen() > 0 || isEntityHasHarmEffects(entity));
    }

    @SubscribeEvent
    public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
        if (event.getEntity() instanceof ServerPlayer serverPlayer) {
            syncPlayerRPG(serverPlayer);
        }
    }

    private static void syncPlayerRPG(ServerPlayer serverPlayer) {
        serverPlayer.getCapability(PlayerRPGCapabilityProvider.CAPABILITY).ifPresent(capability -> {

        });
    }

    private static boolean isEntityHasHarmEffects(LivingEntity living) {
        for (MobEffectInstance activeEffect : living.getActiveEffects()) {
            if (activeEffect.getEffect().getCategory() == MobEffectCategory.HARMFUL) return true;
        }
        return false;
    }

    @SubscribeEvent(priority = EventPriority.LOWEST)
    public static void onLivingDamage(LivingDamageEvent event) {
        if (event.isCanceled()) return;
        //反伤(但是不能无限反）
        if (!event.getSource().is(ModDamageTypes.REFLEXIVE)) runIfLivingDamage(event.getSource(), living -> {
            BSEntityUtil.safeAttribute(event.getEntity(), ModAttributes.REFLEXIVE_INJURY, v -> {
                if (v > 0) {
                    living.hurt(new DamageSource(living.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(ModDamageTypes.REFLEXIVE), event.getEntity(), event.getEntity()), (float) (event.getAmount() * (v / 100f)));
                }
            });
        });
        LivingEntity entity = event.getEntity();
        if (!entity.level().isClientSide() && !event.isCanceled() && FDConfig.shouldShowDamage(entity)) {
            double damage = event.getAmount();
            Component result = damage == Double.POSITIVE_INFINITY ? Component.translatable("damage.POSITIVE_INFINITY") : Component.literal(String.format("%.0f", damage));
            ModUtil.findAllPlayersNearby(player -> {
                var pkg = DamageParticlePackage.formEntity(entity, result, event.getSource());
                var bbh = event.getEntity().getBbHeight();
                if (bbh > 1) {
                    pkg.setScale(-bbh);
                    pkg.move(0, bbh / 2d, 0);
                }
                FlashNet.channel.send(PacketDistributor.PLAYER.with(() -> player), pkg);
            }, entity, 256);
        }
        if (entity instanceof Player player) {
            player.getCapability(PlayerRPGCapabilityProvider.CAPABILITY).ifPresent(capability -> {
                //宝命长存的莳者
                if (capability.isKeyOver4(ModItems.DILLER.setName)) {
                    ModUtil.addMobEffect(player, ModMobEffects.DILLER.get(), 200, 2);
                }
            });
        }
    }

    @SubscribeEvent
    public static void onLivingHurt(LivingHurtEvent event) {
        if (event.isCanceled()) return;
        BSEntityUtil.safeAttribute(event.getEntity(), ModAttributes.DODGE, v -> {
            if (Math.random() < v / 100d) {
                //闪避
                event.setAmount(0);
                event.setCanceled(true);
            }
        });
        //如果是远程伤害
        if (event.getSource().isIndirect()) {
            if (event.getSource().getEntity() instanceof LivingEntity living) {
                //增加远程伤害
                BSEntityUtil.safeAttribute(living, ModAttributes.RANGED_DAMAGE, amount -> {
                    event.setAmount((float) (event.getAmount() * (1 + amount / 100d)));
                });
            }
        }
        if (isEntityWeakness(event.getEntity())) {
            BSEntityUtil.safeAttribute(event.getEntity(), ModAttributes.WEAKNESS_SPECIAL_ATTACK, amount -> {
                event.setAmount((float) (event.getAmount() * (1 + amount / 100d)));
            });
        }

        //赛博判定
        runIfPlayerDamage(event.getSource(), (player, capability) -> {
            if (capability.isKeyOver4(ModItems.WARMONGER.getSetName())) {
                int level = 0;
                if (player.hasEffect(ModMobEffects.ANGER.get()))
                    level = Math.min(player.getEffect(ModMobEffects.ANGER.get()).getAmplifier() + 1, 9);
                player.addEffect(new MobEffectInstance(ModMobEffects.ANGER.get(), 400, level));
            }
            if (capability.isKeyOver4(ModItems.IRASSIAN.getSetName())) {
                if (isEntityWeakness(event.getEntity())) {
                    event.setAmount(event.getAmount() * 1.25f);
                }
            }
            if (capability.isKeyOver4(ModItems.CYBREX.getSetName())) {
                double random = player.getRandom().nextDouble();
                if (random < 0.2) {
                    event.setAmount(event.getAmount() * 1.2f);
                } else if (random < 0.69) {

                } else if (random < 0.84) {
                    player.heal(player.getMaxHealth() * 0.1f);
                } else if (random < 0.99) {
                    player.hurt(player.level().damageSources().magic(), Math.min(Math.max(0, player.getHealth() - 1), player.getMaxHealth() * 0.1f));
                } else {
                    event.setAmount(event.getAmount() + player.getMaxHealth() * 10);
                    player.hurt(player.level().damageSources().magic(), Math.min(Math.max(0, player.getHealth() - 1), player.getMaxHealth() * 0.99f));
                    player.sendSystemMessage(Component.translatable("msg.flashdays.cyberlexer.hit"));
                }
            }
            //bloodthirsty
            if (capability.isKeyOver4(ModItems.BLOODTHIRSTY.getSetName())) {
                if (!player.hasEffect(ModMobEffects.BLOODTHIRSTY.get())) {
                    var dmg = Math.min(player.getMaxHealth() * 0.15f, player.getHealth() - 0.1f);
                    player.hurt(player.level().damageSources().magic(), dmg);
                    player.addEffect(new MobEffectInstance(ModMobEffects.BLOODTHIRSTY.get(), 70, 0));
                }
                event.setAmount(event.getAmount() + player.getMaxHealth() * 0.1f);
            }
        });
        //巴奥buff
        if (event.getEntity() instanceof Player player) {
            player.getCapability(PlayerRPGCapabilityProvider.CAPABILITY).ifPresent(capability -> {
                String setName = ModItems.LAST_BAOL.getSetName();
                var effect = ModMobEffects.BAOL_CD.get();
                if (player.getHealth() <= player.getMaxHealth() * 0.1f && capability.isKeyOver4(setName) && !player.hasEffect(effect)) {
                    player.addEffect(new MobEffectInstance(effect, 20 * 60, 0));
                    player.heal(player.getMaxHealth() * 0.4f);
                }
            });
        }
        BSEntityUtil.safeAttribute(event.getEntity(), ModAttributes.DAMAGE_RESISTANCE, amount -> {
            event.setAmount((float) (event.getAmount() * Math.max(0, 1 - amount / 100d)));
        });

    }

    @SubscribeEvent
    public static void onAttachCapability(AttachCapabilitiesEvent<Entity> event) {
        if (event.getObject() instanceof Player player) {
            event.addCapability(new ResourceLocation(FlashDays.MODID, "curios_sets"), new PlayerRPGCapabilityProvider(player));
        }
    }


    @SubscribeEvent
    public static void onLivingTick(LivingEvent.LivingTickEvent event) {
        if (event.isCanceled()) return;
        //流血
        LivingEntity entity = event.getEntity();
        if (entity.hasEffect(ModMobEffects.BLEED.get())) {
            MobEffectInstance instance = entity.getEffect(ModMobEffects.BLEED.get());
            if (instance != null) {
                if (entity.level().getGameTime() % 20 == 0)
                    entity.hurt(new DamageSource(entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(ModDamageTypes.BLEED)), entity.getMaxHealth() * 0.01f * (instance.getAmplifier() + 1));
            }
        }
        if (entity.hasEffect(ModMobEffects.FAST_FALL.get())) {
            if (entity.level().getGameTime() % 5 == 0) if (entity.getDeltaMovement().y < 0) {
                entity.setDeltaMovement(entity.getDeltaMovement().add(0, -1, 0));
            }
        }
    }

    @SubscribeEvent(priority = EventPriority.LOWEST)
    public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event) {
        if (event.phase == TickEvent.Phase.START) return;
        event.player.getCapability(PlayerRPGCapabilityProvider.CAPABILITY).ifPresent(capability -> {
            //cd计算
            capability.tick();
            //每20tick判定一次，降低频率
            if (event.player.level().getGameTime() % 20 == 0) {
                //银河之星被动
                if (capability.isKeyOver4(ModItems.GALATRON.setName))
                    selectPlayersAndExecute(event.player, false, 10, serverPlayer -> serverPlayer.addEffect(new MobEffectInstance(ModMobEffects.GALATRON.get(), 20, 0, false, false)));
                //贾沃疹
                if (capability.isKeyOver4(ModItems.IRASSIAN.setName))
                    selectEntityAndExecute(event.player, true, 10, living -> living.addEffect(new MobEffectInstance(ModMobEffects.IRASSIAN.get(), 20, 0)));
            }
        });
    }

    @SubscribeEvent(priority = EventPriority.LOWEST)
    public static void onEntityHeal(LivingHealEvent event) {
        if (event.isCanceled()) return;
        if (event.getEntity() instanceof Player player) {
            player.getCapability(PlayerRPGCapabilityProvider.CAPABILITY).ifPresent(capability -> {
                //灵能档案群体恢复
                float rec = event.getAmount() * 0.1f;
                //判断恢复大于0.5防止递归
                if (capability.isKeyOver4(ModItems.ZRO_CRYSTAL.getSetName()) && rec > 0.5f) {
                    selectPlayersAndExecute(player, true, 10, serverPlayer -> serverPlayer.heal(rec));
                }
            });
        }
        LivingEntity entity = event.getEntity();
        if (!entity.level().isClientSide() && !event.isCanceled() && FDConfig.shouldShowDamage(entity)) {
            String result = String.format("+ %.0f", event.getAmount());
            ModUtil.findAllPlayersNearby(player -> {
                FlashNet.channel.send(PacketDistributor.PLAYER.with(() -> player), DamageParticlePackage.formEntity(entity, result, "heal"));
            }, entity, 256);
        }
    }

    @SubscribeEvent(priority = EventPriority.HIGHEST)
    public static void onPlayerClone(PlayerEvent.Clone event) {
        Player original = event.getOriginal();
        Player newOne = event.getEntity();
        original.reviveCaps();
        copyCapability(PlayerRPGCapabilityProvider.CAPABILITY, original, newOne);
        original.invalidateCaps();
    }

    @SubscribeEvent(priority = EventPriority.HIGHEST)
    public static void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event) {

    }

    @SuppressWarnings("all")
    private static <V extends Tag, T extends INBTSerializable<V>> void copyCapability(Capability<T> capability, Player from, Player to) {
        from.getCapability(capability).ifPresent(cap -> {
            to.getCapability(capability).ifPresent(p -> {
                p.deserializeNBT(cap.serializeNBT());
            });
        });
    }

}
